﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Threading;
using FullDuplexTest.Contracts;

namespace FullDuplexTest.Silverlight.Web
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class Chat : IChat
    {
        private static readonly IDictionary<Guid, IChatResponse> ResponseCallbacks = new Dictionary<Guid, IChatResponse>();
        private static readonly IDictionary<Guid, IContextChannel> ResponseChannels = new Dictionary<Guid, IContextChannel>();

        public void Send(ChatMessage message)
        {
            var channels = ResponseCallbacks.Keys;

            foreach (var channel in channels)
            {
                try
                {
                    ResponseCallbacks[channel].Notify(message);
                }
                catch (KeyNotFoundException)
                {
                }
                catch
                {
                    ChannelUnusable(ResponseChannels[channel], null);
                }
            }
        }

        public void Connect(Guid machineIdentifier)
        {
            var channel = OperationContext.Current.Channel;
            var callback = OperationContext.Current.GetCallbackChannel<IChatResponse>();
            ResponseChannels[machineIdentifier] = channel;
            ResponseCallbacks[machineIdentifier] = callback;
            channel.Faulted += ChannelUnusable;
            channel.Closing += ChannelUnusable;
            new Thread(() => KeepAlive(machineIdentifier)).Start();
        }

        private static void ChannelUnusable(object sender, EventArgs e)
        {
            var channel = (IContextChannel)sender;
            channel.Faulted -= ChannelUnusable;
            channel.Closing -= ChannelUnusable;
            var match = ResponseChannels.FirstOrDefault(x => x.Value.RemoteAddress.Uri == channel.RemoteAddress.Uri);

            if (!Equals(match, default(KeyValuePair<Guid, IContextChannel>)))
            {
                return;
            }

            var key = match.Key;
            ResponseCallbacks.Remove(key);
            ResponseChannels.Remove(key);

            try
            {
                channel.Close();
            }
            catch
            {
                Thread.Sleep(1);
            }
        }

        private static void KeepAlive(Guid machine)
        {
            try
            {
                var channel = ResponseCallbacks[machine];

                while (ResponseCallbacks.ContainsKey(machine))
                {
                    channel.Ping();
                    Thread.Sleep(5000);
                }
            }
            catch
            {
                try
                {
                    if (ResponseChannels.ContainsKey(machine))
                    {
                        ChannelUnusable(ResponseChannels[machine], null);
                    }
                }
                catch
                {
                    Thread.Sleep(1);
                }
            }
        }
    }
}
